<template>
  <div></div>
</template>

<script setup lang="ts">
  // IndexedDB
  function demo() {
    // 数据库名称，版本号
    // 创建或打开数据库，指定数据库名称和版本号，版本号为整数，默认值为 1，若指定的数据库不存在，就会新建数据库
    // 若指定版本大于当前版本，就会触发 onupgradeneeded 事件
    let db: IDBDatabase
    const request: IDBOpenDBRequest = indexedDB.open('database', 2)

    // 数据库打开失败
    request.onerror = (err) => {
      console.log('数据库打开失败', err)
    }

    // 数据库打开成功
    request.onsuccess = (event: any) => {
      console.log('数据库打开成功', event)
      db = request.result
      // // 创建事务，指定数据库对象存储名称，操作模式为读写模式
      // const transaction = db.transaction(['user'], 'readwrite')
      // // 获取对象存储
      // const store = transaction.objectStore('user')

      // 事务的执行顺序是安装创建的顺序，而不是请求的顺序
      const transaction = db.transaction(['user'], 'readwrite')
      const transaction2 = db.transaction(['user'], 'readwrite')

      const store = transaction.objectStore('user')
      const store2 = transaction2.objectStore('user')

      store2.put('ikun', 'name')
      store.put('kunkun', 'name')

      // 添加数据
      // addData({ name: 'kunkun', age: 18, userType: 'user', sort: 10 })

      // getDataByKey('kunkun')

      // 删除数据
      // delDataByKey('kunkun')

      // 清空所有数据
      // delAllData()

      // 根据索引查询数据
      // getDataByKey(2)
      // getDataByIndex('name', 'kunkun')

      // 获取所有数据
      // getAllData()

      // // 更新数据
      // putDataByKey(1, { name: '坤坤' })

      // IndexedDB 的读写操作是异步的，通过监听请求的 onsuccess 和 onerror 事件来获取读写的结果

      // 新增数据
      /*
      const addRequest = store.add({ name: 'kunkun', age: 18, userType: 'user', sort: 10 })
      addRequest.onsuccess = (event) => {
        console.log('添加数据成功')
      }
      addRequest.onerror = (err) => {
        console.log('添加数据失败', err)
      }
    */

      // 修改数据
      /*
      const updateRequest = store.put({ name: '坤坤', age: 18, userType: 'user', sort: 10 }, 1)
      updateRequest.onsuccess = (event) => {
        console.log('修改数据成功')
      }
      updateRequest.onerror = (err) => {
        console.log('修改数据失败', err)
      }
    */

      // 获取所有数据
      /*
      const getAllRequest = store.getAll()
      getAllRequest.onsuccess = (event: any) => {
        console.log('获取所有数据成功', event.target.result)
      }
      getAllRequest.onerror = (err) => {
        console.log('获取所有数据失败', err)
      }
    */

      // 根据 key 获取数据
      /*
      const getRequest = store.get(1)
      getRequest.onerror = (err) => {
        console.log('获取数据失败', err)
      }
      getRequest.onsuccess = (event: any) => {
        console.log('获取数据成功', event.target.result)
      }
    */

      // 根据 key 删除数据
      /*
     const delRequest = store.delete(1)
      delRequest.onerror = (err) => {
        console.log('删除数据失败', err)
      }

      delRequest.onsuccess = (event) => {
        console.log('删除数据成功')
      }
    */

      // 删除所有数据
      /*
      const clearRequest = store.clear()
      clearRequest.onsuccess = (event) => {
        console.log('删除所有数据成功')
      }
      clearRequest.onerror = (err) => {
        console.log('删除所有数据失败', err)
      }
    */

      // 使用索引

      /*
        const index = store.index('name')
        const getRequest = index.get('kunkun')
        getRequest.onsuccess = (event: any) => {
          console.log('使用索引获取数据成功', event.target.result)
        }
        getRequest.onerror = (err) => {
          console.log('使用索引获取数据失败', err)
        }
      */

      // 遍历数据
      const cursorRequest = store.openCursor()
      cursorRequest.onsuccess = (event: any) => {
        let cursor = event.target.result
        if (cursor) {
          // 遍历数据
          console.log(cursor.value)
          // 光标移到下一个对象，如果是最后一个，光标指向 null
          cursor.continue()
        }
        // console.log('遍历数据成功', event.target.result)
      }
      cursorRequest.onerror = (err) => {
        console.log('遍历数据失败', err)
      }
    }

    // 第一次打开数据库，或者数据版本发送变化时触发
    // 第一次打开数据库，会先触发 onupgradeneeded ，然后触发 onsuccess
    request.onupgradeneeded = (event: any) => {
      console.log('新建数据库，或者版本升级时触发', event)
      let db: IDBDatabase = event.target.result
      let objectStore: IDBObjectStore
      // 判断数据库中是否存在名为 user 的对象存储
      if (!db.objectStoreNames.contains('user')) {
        // 创建对象存储对象 user，设置主键自增
        objectStore = db.createObjectStore('user', { autoIncrement: true })
        // 指定主键为 id，第一条记录的 id 为 1
        // objectStore = db.createObjectStore('user', { keyPath: 'id' })
        // 创建索引，索引名为 name，设置唯一索引
        // objectStore.createIndex 的三个参数分别为 索引名称、索引所在的属性、配置对象（该属性是否可以重复）
        // objectStore.createIndex('name', 'name', { unique: true })
      }
    }

    // 上一次连接的数据库为关闭
    request.onblocked = (event) => {
      console.log('数据库被另一个连接打开，且阻止了当前连接继续操作', event)
    }

    // 3 秒后删除数据库
    // setTimeout(() => {
    //   const delRequest = indexedDB.deleteDatabase('database')

    //   delRequest.onsuccess = (event) => {
    //     console.log('数据库删除成功', event)
    //   }

    //   delRequest.onerror = (err) => {
    //     console.log('数据库删除失败', err)
    //   }
    // }, 5000)

    // 关闭数据库
    // setTimeout(() => {
    //   db.close()
    // }, 3000)

    // 比较两个值是否为 indexedDb 的相同的主键，0表示相同，1 表示第一个主键大于第二个主键，-1 表示第一个主键小于第二个主键
    // 方法参数不支持布尔值和对象
    console.log(indexedDB.cmp(1, 2), 'comp')

    // 封装使用
    function addData(value: any, key?: IDBValidKey) {
      const transaction = db.transaction(['user'], 'readwrite')
      const store = transaction.objectStore('user')
      const request = store.add(value, key)

      request.onsuccess = (event) => {
        console.log('添加成功', event)
      }

      request.onerror = (error) => {
        console.log('添加失败', error)
      }
    }

    // 根据索引删除数据
    function delDataByKey(key: IDBValidKey | IDBKeyRange) {
      const transaction = db.transaction(['user'], 'readwrite')
      const store = transaction.objectStore('user')

      const request = store.delete(key)
      request.onsuccess = (event) => {
        console.log(`根据索引${key}删除数据成功`, event)
      }

      request.onerror = (error) => {
        console.log(`根据索引${key}删除数据失败`, error)
      }
    }

    // 清空数据所有数据
    function delAllData() {
      const transaction = db.transaction(['user'], 'readwrite')
      const store = transaction.objectStore('user')
      const request = store.clear()

      request.onsuccess = (event) => {
        console.log('清空数据成功', event)
      }

      request.onerror = (error) => {
        console.log('清空数据失败', error)
      }
    }

    // 更新数据
    function putDataByKey(key: IDBValidKey, value: any) {
      const transaction = db.transaction(['user'], 'readwrite')
      const store = transaction.objectStore('user')
      const request = store.put(value, key)

      request.onsuccess = (event) => {
        console.log(`根据索引${key}更新数据成功`, event)
      }

      request.onerror = (error) => {
        console.log(`根据索引${key}更新数据失败`, error)
      }
    }

    function getDataByKey(key: IDBValidKey | IDBKeyRange) {
      const transaction = db.transaction(['user'], 'readwrite')
      const store = transaction.objectStore('user')
      const request = store.get(key)

      request.onsuccess = (event) => {
        console.log(`根据索引${key}获取数据成功`, (event.target as any).result)
      }

      request.onerror = (error) => {
        console.log(`根据索引${key}获取数据失败`, error)
      }
    }

    function getDataByIndex(name: string, key: IDBValidKey | IDBKeyRange) {
      const transaction = db.transaction(['user'], 'readwrite')
      const store = transaction.objectStore('user')
      const index = store.index(name)
      const request = index.get(key)

      request.onsuccess = (event) => {
        console.log(`根据索引${key}获取数据成功`, (event.target as any).result)
      }

      request.onerror = (error) => {
        console.log(`根据索引${key}获取数据失败`)
      }
    }

    // 获取全部数据
    function getAllData() {
      const transaction = db.transaction(['user'], 'readwrite')
      const store = transaction.objectStore('user')
      const request = store.getAll()

      request.onsuccess = (event) => {
        console.log('获取全部数据成功', (event.target as any).result)
      }

      request.onerror = (error) => {
        console.log('获取全部数据失败', error)
      }
    }

    // promise 版本封装
    function getData(
      storeNname: string | string[],
      key: IDBValidKey | IDBKeyRange,
      mode: IDBTransactionMode = 'readwrite'
    ) {
      return new Promise((resolve, reject) => {
        if (typeof storeNname === 'string') {
          storeNname = [storeNname]
        }
        const transaction = db.transaction(storeNname, mode)
        const store = transaction.objectStore('user')
        const request = store.get(key)
        request.onsuccess = (event) => {
          resolve((event.target as any).result)
          console.log(`根据索引${key}获取数据成功`, (event.target as any).result)
        }

        request.onerror = (error) => {
          reject(error)
          console.log(`根据索引${key}获取数据失败`, error)
        }
      })
    }
  }

  demo()

  // indexedDb 是采用异步监听回调的处理机制，在项目中使用起来不是很方便，在这里使用 promise 进行简易封装
  class IndexedDBWrapper {
    // 数据库名称
    private dbName: string
    // 存储对象名
    private storeName: string | string[]
    // 数据数实例
    private db: IDBDatabase | null
    // 版本号
    private version: number

    constructor(dbName: string, storeName: string | string[], version: number = 1) {
      this.dbName = dbName
      this.storeName = storeName
      this.version = version
      this.db = null
      this.open()
    }

    // 打开数据库
    open(): Promise<IDBDatabase> {
      return new Promise((resolve, reject) => {
        const request = indexedDB.open(this.dbName, this.version)

        request.onupgradeneeded = (event: any) => {
          const db: IDBDatabase = event.target.result
          if (typeof this.storeName === 'string') {
            if (!db.objectStoreNames.contains(this.storeName)) {
              db.createObjectStore(this.storeName, { keyPath: 'id', autoIncrement: true })
            }
          } else {
            this.storeName.forEach((name) => {
              if (!db.objectStoreNames.contains(name)) {
                db.createObjectStore(name, { keyPath: 'id', autoIncrement: true })
              }
            })
          }
        }
        request.onsuccess = (event: any) => {
          this.db = event.target.result
          resolve(this.db!)
        }
        request.onerror = (event: any) => {
          reject(event.target.error)
        }
      })
    }

    // 获取值
    getItem(storeName: string, key: number): Promise<any> {
      return new Promise(async (resolve, reject) => {
        if (!this.db) {
          this.db = await this.open()
        }
        const transaction = this.db.transaction(storeName, 'readonly')
        const store = transaction.objectStore(storeName)
        const request = store.get(key)
        request.onsuccess = (event: any) => {
          resolve(event.target.result)
        }
        request.onerror = (event: any) => {
          reject(event.target.error)
        }
      })
    }

    // 存储值
    setItem(storeName: string, data: any): Promise<any> {
      return new Promise(async (resolve, reject) => {
        if (!this.db) {
          this.db = await this.open()
        }
        const transaction = this.db.transaction(storeName, 'readwrite')
        const store = transaction.objectStore(storeName)
        const request = store.put(data)
        request.onsuccess = (event: any) => {
          resolve(event.target.result)
        }
        request.onerror = (event: any) => {
          reject(event.target.error)
        }
      })
    }

    // 删除值
    removeItem(storeName: string, key: number): Promise<any> {
      return new Promise(async (resolve, reject) => {
        if (!this.db) {
          this.db = await this.open()
        }
        const transaction = this.db.transaction(storeName, 'readwrite')
        const store = transaction.objectStore(storeName)
        const request = store.delete(key)
        request.onsuccess = (event: any) => {
          resolve(event.target.result)
        }
        request.onerror = (event: any) => {
          reject(event.target.error)
        }
      })
    }

    // 清空值
    clear(storeName: string): Promise<any> {
      return new Promise(async (resolve, reject) => {
        if (!this.db) {
          this.db = await this.open()
        }
        const transaction = this.db.transaction(storeName, 'readwrite')
        const store = transaction.objectStore(storeName)
        const request = store.clear()
        request.onsuccess = (event: any) => {
          resolve(event.target.result)
        }
        request.onerror = (event: any) => {
          reject(event.target.error)
        }
      })
    }

    // 删除数据库
    del(): Promise<any> {
      return new Promise((resolve, reject) => {
        this.close()
        const request = indexedDB.deleteDatabase(this.dbName)
        request.onsuccess = (event: any) => {
          console.log('删除数据库成功', event.target.result)
          resolve(event.target.result)
        }
        request.onerror = (event: any) => {
          console.log('删除数据库失败')
          reject(event.target.error)
        }

        request.onupgradeneeded = (e) => {
          console.log('升级数据库', e)
        }
      })
    }

    // 关闭数据库
    close() {
      this.db?.close()
    }
  }

  // const dbWrapper = new IndexedDBWrapper('database', ['user', 'task'], 2)

  // 测试
  // dbWrapper
  //   .setItem('user', { id: 1, name: '张三' })
  //   .then((res) => {
  //     console.log('存储成功', res)
  //   })
  //   .catch((err) => {
  //     console.log('存储失败', err)
  //   })

  // dbWrapper
  //   .getItem('user', 1)
  //   .then((res) => {
  //     console.log('获取成功', res)
  //   })
  //   .catch((err) => {
  //     console.log('获取失败', err)
  //   })

  // dbWrapper
  //   .removeItem('user', 1)
  //   .then((res) => {
  //     console.log('删除成功', res)
  //   })
  //   .catch((err) => {
  //     console.log('删除失败', err)
  //   })

  // setTimeout(() => {
  //   console.log('删除数据库')
  //   dbWrapper.close()
  //   dbWrapper
  //     .del()
  //     .then((res) => {
  //       console.log('删除数据库成功', res)
  //     })
  //     .catch((err) => {
  //       console.log('删除数据库失败', err)
  //     })
  // }, 3000)
</script>

<style scoped></style>
